﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace VersionComparer.DataModel.Assemblies
{

    [Serializable]
    public class AssemblyItemsInfo : AssemblyItemBase<Assembly>
    {
        
        public AssemblyItemsInfo(Assembly assembly)
            : base()
        {
            base.InitializeAssemblyItem(assembly);
            
        }
        protected override string GetFullText(Assembly value)
        {
            StringBuilder builder = new StringBuilder ();
            int index = 0;
            foreach (var child in GetChilds())
            {
                if (index == 24)
                {
                
                }
                string fullText = child.FullText;
                //builder.AppendFormat("{0}" ,fullText);
                builder.AppendLine(fullText);
                index++;
            }
            return builder.ToString();
        }

        private class Wrapper <T>
        {
            public Wrapper ( T wrappedValue)
            {
                WrappedValue = wrappedValue;
            }
            public T WrappedValue
            {
                get;
                private set;
            }
            public object Tag
            {
                get;
                set;
            }

            public static Wrapper<T>[] FromArray(T[] valuesToWrap)
            {
                if ( valuesToWrap == null )
                    return null;
                Wrapper<T>[] result = new  Wrapper<T>[valuesToWrap.Length];
                for (int i = 0; i < valuesToWrap.Length; i++)
                    result[i] = new Wrapper<T>(valuesToWrap[i]);
                return result;

            }
        }
        private void CreateType(Wrapper<Type> type)
        {
            if (type.WrappedValue.DeclaringType == null)
                type.Tag = CreateType(type.WrappedValue, this );
        }

        protected override ComparableItem[] GetChilds(Assembly assembly)
        {
            Stopwatch s = new Stopwatch();
            s.Start();
            List<ComparableItem> items = new List<ComparableItem>();
            items.Add(new AssemblyValueItem(null, "FullName", assembly.FullName));
            //items.Add(new AssemblyValueItem(null, "RuntimeVersion", assembly.ImageRuntimeVersion));
            items.Add(new AssemblyValueItem(null, "EntryPoint", assembly.EntryPoint == null ? "" : assembly.EntryPoint.Name));
            items.Add(new AssemblyValueItem(null, "ProcessorArchitecture", assembly.GetName().ProcessorArchitecture.ToString()));
            items.Add(new AssemblyValueItem(null, "Version", assembly.GetName().Version.ToString()));
            double d2 = s.ElapsedMilliseconds;
            foreach (string resource in assembly.GetManifestResourceNames())
                items.Add(new ResourceItem(null, resource));
            double d3 = s.ElapsedMilliseconds;
            var referenceAssemblies = assembly.GetReferencedAssemblies();
            foreach (var reference in referenceAssemblies)
            {
                items.Add(Create<AssemblyReferenceItem, AssemblyName>(reference));
            }
            double d4 = s.ElapsedMilliseconds;
            /*
            var wrappers = Wrapper<Type>.FromArray(assembly.GetTypes ());
            Parallel.ForEach(wrappers, CreateType);
            foreach (var value in wrappers)
            {
                if (value.Tag != null)
                    items.Add(value.Tag as TypeItem);
            }
             * */
            
            foreach (Type type in assembly.GetTypes())
            {
                if (type.DeclaringType == null)
                    items.Add(CreateType(type, this));
            }
            
            double d5 = s.ElapsedMilliseconds;
            return items.ToArray();
        }
        protected override CustomAttributeData[] GetAttributes(Assembly value)
        {
            return  CustomAttributeData.GetCustomAttributes(value).ToArray();
        }
        protected override void BuildRtf(Base.RtfBuilder builder, bool differencesOnly, bool useParentColor)
        {
            var childs = differencesOnly ? SubItems : GetChilds();
            if (childs != null)
            {
                foreach (var child in childs)
                    child.GetAsRtf(builder, differencesOnly, useParentColor);
            }
        }
    }


    
    [Serializable]
    public class AssemblyItem : AssemblyItemBase<Assembly>
    {

        DateTime lastRead;
        AssemblyItemsInfo info;
        public AssemblyItem()
        {
        }

        public class AssemblyLoader : MarshalByRefObject
        {
            Dictionary<string, Type> knownTypes = new Dictionary<string, Type>();
            Assembly Assembly
            {
                get;
                set;
            }
            public AssemblyItemsInfo Load(string path)
            {
                try
                {
                    Stopwatch s = new Stopwatch();
                    s.Start ();
                    
                    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
                    AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_AssemblyResolve;
                    double d1 = s.ElapsedMilliseconds;
                    Assembly assembly = Assembly.LoadFrom(path);
                    Assembly = assembly;
                    double d2 = s.ElapsedMilliseconds;
                    var result = new AssemblyItemsInfo(assembly);
                    double d3 = s.ElapsedMilliseconds;
                    return result;
                }
                catch (Exception )
                {
                    return null;
                }
            }

            public string[] GetTypes()
            {
                knownTypes.Clear();
                foreach (Type type in Assembly.GetTypes())
                    knownTypes.Add(type.FullName, type);
                return knownTypes.Keys.ToArray();
            }

            /*
            public TypeItem CreateTypeItem(string typeName)
            {
            }*/

            Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                return null;
            }

            
        }

        public AssemblyItem(string path, Guid id)
        {
            Path = path;
            Id = id;
        }

        
        string Path
        {
            get;
            set;
        }

        public override ComparableItem[] SubItems
        {
            get
            {
                if (info == null)
                    return null;
                return info.SubItems;
            }
            set
            {
                if ( info != null )
                    info.SubItems = value;
            }
        }

        public override bool RequiresUpdate
        {
            get
            {
                return lastRead != File.GetLastWriteTimeUtc (Path);
                
            }
        }
        public override void Refresh()
        {
            base.Refresh();
            try
            {
                System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
                s.Start();
                AppDomain appDomain = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
                double d2 = s.ElapsedMilliseconds;
                AssemblyLoader loader = (AssemblyLoader)appDomain.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName);
                double d3 = s.ElapsedMilliseconds;
                info = loader.Load(Path);
                double d4 = s.ElapsedMilliseconds;
                info.Parent = this;
                AppDomain.Unload(appDomain);
                double d5 = s.ElapsedMilliseconds;
                lastRead = File.GetLastWriteTimeUtc(Path);
                double d6 = s.ElapsedMilliseconds;
                FullText = info.FullText;
            }
            catch (Exception )
            {
                throw;
            }

        }
        protected override System.Drawing.Image GetImage()
        {
            try
            {
                if (File.Exists(Path))
                    return Icon.ExtractAssociatedIcon(Path).ToBitmap();
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }

        public override ComparableItem Parent
        {
            get
            {
                return null;
            }
        }
        protected override ComparableItem[] CreateChilds()
        {
            return info.GetChilds();
        }

        protected override void BuildRtf(Base.RtfBuilder builder, bool differencesOnly, bool useParentColor)
        {
            info.GetAsRtf(builder, differencesOnly, useParentColor);
        }


    }
}
